Microsoft System Center Configuration Manager (SCCM) 2012 has a very powerful Application Detection and Delivery model, separate from the existing ‘package and program delivery model’ of previous versions of SCCM & SMS.
The power of this new model is not having to ‘daisy chain’ packages and executables together to achieve a desired outcome. Using SCCM’s Detection Model reduces the burden in managing a Windows client base in terms of keeping its baseline configuration the same across every client in the Organisation.
I recently assisted a Kloud customer to configure a script delivery application, that was using the Application delivery model and the ‘Detection Method’ to ensure files reached their local Windows 8 folder destinations successfully. The script simply copies the files where they need to go and the Detection Method then determines the success of that script. If SCCM does not detect the files in their correct destination locations, it attempts again at executing the script.
Benefits in using SCCM 2012 Application and Detection Method Delivery
Using this Application and Detection method provided Kloud’s customer with the following business benefits:
- Increased reliability of delivering Office template files to a Windows 8 machine and therefore reduced TCO in delivering software to authorised workstations. If the application files were corrupted or deleted during installation or post-installation (for example a user turning their workstation off during an install), then SCCM detects these files are missing and re-runs the installation
- Upgrades are made easier, as it does not depend on any Windows 8 workstation having to run a previous installation or ‘package’. The ‘Detection Method’ of the Application object determines if the correct file version is there (or not) and if necessary re-runs the script to deliver the files. The ‘Detection Method’ also runs after every install, to guarantee that a client is 100% compliant with that application delivery.
- Uses SCCM client agent behaviour including BITS, restart handling, use of the ‘Software Center’ application for user initiated installs and Application package version handling – for example, if a single file is updated in the Application source and re-delivered to the Distribution Point, the SCCM client detects a single file has changed, and will only downloads the changed file saving bandwidth (and download charges) from the Distribution Point
Customer Technical Requirements
Kloud’s customer had the following technical requirements:
1. My customer wanted to use an SCCM Application and Detection Rule to distribute ten Office 2010 template files to Windows 8 workstations (managed with the SCCM client)
2. They wanted to be able to drop new Office 2010 template files at any stage into the SCCM source application folder, distribute the application and the SCCM clients download and install those new templates with minimum interference to end users.
3. They also wanted the minimum number of objects in SCCM to manage the application, and wanted the application to ‘self heal’ if a user deleted any of the template files.
4. All code had to be written in PowerShell for ease of support.
Limitations of Native Detection Methods
SCCM 2012 has a great native Detection Rules method for MSI files and file system executables (see native Detection Rule image below:).
However we quickly worked out its limitations with this native Detection Rule model, namely for the ‘File System’ setting type:
1. Environment variables for user accounts, such as %username% and %userprofile% are not supported
2. File versioning can only work with Windows executables (ie. .EXE) and not metadata embedded in files, for example Word files.
SCCM comes with the ability to run Powershell, VBScript or JScript as part of its Detection Model, and it is documented with VBScript examples at this location:
Taking these examples, the critical table to follow to get the Detection Model working correctly (and improving your understanding of how your script works in terms of ‘error code’, ‘stdout’ and ‘stderror’) is the following table, kindly reproduced from Microsoft from the TechNet Link above:
Script exit code | Data read from STDOUT | Data read from STDERR | Script result | Application detection state |
---|---|---|---|---|
0 | Empty | Empty | Success | Not installed |
0 | Empty | Not empty | Failure | Unknown |
0 | Not empty | Empty | Success | Installed |
0 | Not empty | Not empty | Success | Installed |
Non-zero value | Empty | Empty | Failure | Unknown |
Non-zero value | Empty | Not empty | Failure | Unknown |
Non-zero value | Not empty | Empty | Failure | Unknown |
Non-zero value | Not empty | Not empty | Failure | Unknown |
This table tells us that the key to achieving an Application delivery ‘success’ or ‘failure’ using our PowerShell Detection script boils down to achieving either of the rows highlighted in red – any other result (i.e. “Unknown” for the “Application Detection State”) will simply just result in the application not delivering to the client.
The critical part of any Detection Model script is to ensure an error code of ‘0’ is always the result, regardless if the application is installed successfully or has failed. The next critical step is the Powershell object equivalent of populating the ‘stdout’ object. Other script authors may choose to test the ‘stderror’ object as well in their scripts, but I found it unnecessary and preferred to ‘keep it simple’.
After ensuring my script achieved an exit code of ‘0’, I then concentrated on my script either populating the ‘stdout’ object or not populating the ‘stdout’ object – I essentially ignored the ‘stderror’ object completely and ensured my script ran ‘error free’. At all times, for example, I used ‘test-path’ to first test to see a file or folder exists before then attempting to grab its metadata properties. If I didn’t use ‘test-path’, then the script would error if a file or folder was not found and then it would end up in an “unknown” detection state.
I therefore solely concentrated on my script achieving only the highlighted rows (in red) of the table above.
Microsoft provides example of VBScript code to populate the ‘stdout’ (and ‘stderror’) objects and can be found in the TechNet link above – however my method involves just piping a single PowerShell ‘write-host’ command if the Detection Script determines the application has been delivered successfully. This satisfies populating the ‘stdout’ object and therefore achieving Detection success.
Limitations in using Scripts for Detection
There were two issues in getting a Detection Method working properly: an issue related to the way SCCM delivers files to the local client (specifically upgrades) and an issue with the way Office template files are used.
One of the issues we have is that Word and Excel typically changes a template file (however small the change!) when either application is loaded, by changing either its ‘Date Modified’ timestamp or modifying the file length in bytes of the file (or both). Therefore, using a detection method that determines whether a file has been delivered successfully to the workstation should avoid using a file’s length in byes or its modified timestamp.
The other issue we found is that SCCM has a habit of changing the ‘Date Modified’ timestamp of all files it delivers when it detects an ‘upgrade’ of the source files for that application. It typically does not touch the timestamp of the source files if it delivers a brand new install to a client that has never received the software, however if a single file in the source folder is changed for that application, then SCCM tries to use a previous version of the application in the cache (C:\windows\ccmcache) and only downloads the new file that has change. This results in all files having their ‘Data Modified’ timestamp changing (except for the brand new file). Therefore determining if that application has delivered successfully using ‘Date Modified’ timestamps is not recommended. The key to seeing this process in action is looking at the file properties in the C:\windows\ccmcache\<sccm code> folder for that application, particularly before and after a file is updated in the original source SCCM application folder.
Ultimately, for Kloud’s customer, we used a file’s Metadata to determine the file version and whether the application has been delivered successfully or not. In this example, we used the ‘Company’ metadata field of the Word and Excel template file (found under a file’s ‘Properties’):
I used this Scripting Guy’s TechNet Blog to form the basis of retrieving a file’s metadata using a PowerShell function, and then using that information pulled from the file to determine a good attribute to scan for, in terms of file version control.
One of the limitations I found was that this function (through no fault of its author: Ed Wilson!) does not return ‘Version number’, so we used the ‘Company’ field instead. If someone has worked out a different PowerShell method to retrieve that ‘Version number’ metadata attribute, then feel free to tell me in the comments section below!
The next step in getting this PowerShell script to work correctly, is ensuring that only ‘error code = 0’ is returned when this script is executed. Any other error code will result in breaking the delivery of that application to the client. The next step is then only ensuring that a ‘write-host’ is executed if it detects that all detected files are installed – in this example, only 10 files that are 100% detected in my array ‘Path’ will result in a ‘write-host’ being sent to the SCCM client and therefore telling SCCM that client has been successfully delivered. If I were to copy that Powershell script locally, run that script and not detect all files on that machine, then that script will not display anything to that Powershell window. This tells the SCCM client that the delivery has failed. If that script ran locally and only a single ‘write-host’ of ‘all files accounted for!’ was shown to the screen, this tells me the Detection is working.
The sample code for our Detection Method can be found below (all filenames and paths have been changed from my customer’s script for example purposes):
[code language=”powershell”]
# Authors: Michael Pearn & Ed Wilson [MSFT]
Function Get-FileMetaData
{
<#
.Synopsis
This function gets file metadata and returns it as a custom PS Object
#Requires -Version 2.0
#>
Param([string[]]$folder)
foreach($sFolder in $folder)
{
$a = 0
$objShell = New-Object -ComObject Shell.Application
$objFolder = $objShell.namespace($sFolder)
foreach ($File in $objFolder.items())
{
$FileMetaData = New-Object PSOBJECT
for ($a ; $a -le 266; $a++)
{
if($objFolder.getDetailsOf($File, $a))
{
$hash += @{$($objFolder.getDetailsOf($objFolder.items, $a)) =
$($objFolder.getDetailsOf($File, $a)) }
$FileMetaData | Add-Member $hash
$hash.clear()
} #end if
} #end for
$a=0
$FileMetaData
} #end foreach $file
} #end foreach $sfolder
} #end Get-FileMetaData
$TemplateVersions = "5.0.2"
$wordStandards = "C:\Program Files (x86)\Customer\Customer Word Standards"
$wordTemplates = "C:\Program Files (x86)\Microsoft Office\Templates"
$wordTheme = "C:\Program Files (x86)\Microsoft Office\Document Themes 14\Theme Colors"
$excelAddins = "C:\Program Files (x86)\Customer\Customer Excel Addins"
$xlRibbon = "C:\Program Files (x86)\Microsoft Office\Office14\ADDINS"
$PPTribbon = "C:\Program Files (x86)\Customer\PowerPoint Templates"
$PPTtemplates = "C:\Program Files (x86)\Microsoft Office\Templates\Customer"
$strFile1 = "Bridge Template.xlsm"
$strFile2 = "Excel Ribbon.xlam"
$strFile3 = "NormalEmail.dotm"
$strFile4 = "PPT Ribbon.ppam"
$strFile5 = "Client Pitch.potx"
$strFile6 = "Client Presentation.potx"
$strFile7 = "Client Report.potx"
$strFile8 = "Blank.potx"
$strFile9 = "Blocks.dotx"
$strFile10 = "Normal.dotm"
$Path = @()
$Collection = @()
$Path += "$excelAddins\$strfile1"
$Path += "$xlRibbon\$strfile2"
$Path += "$PPTribbon\$strfile3"
$Path += "$PPTtemplates\$strfile4"
$Path += "$PPTtemplates\$strfile5"
$Path += "$PPTtemplates\$strfile6"
$Path += "$wordStandards\$strfile7"
$Path += "$excelAddins\$strfile8"
$Path += "$xlRibbon\$strfile9"
$Path += "$PPTribbon\$strfile10"
if (Test-Path $wordStandards) {
$fileMD = Get-FileMetaData -folder $wordStandards
$collection += $fileMD | select path, company
}
if (Test-Path $wordTemplates) {
$fileMD = Get-FileMetaData -folder $wordTemplates
$collection += $fileMD | select path, company
}
if (Test-Path $wordTheme) {
$fileMD = Get-FileMetaData -folder $wordTheme
$collection += $fileMD | select path, company
}
if (Test-Path $excelAddins) {
$fileMD = Get-FileMetaData -folder $excelAddins
$collection += $fileMD | select path, company
}
if (Test-Path $xlRibbon) {
$fileMD = Get-FileMetaData -folder $xlRibbon
$collection += $fileMD | select path, company
}
if (Test-Path $PPTribbon) {
$fileMD = Get-FileMetaData -folder $PPTribbon
$collection += $fileMD | select path, company
}
if (Test-Path $PPTtemplates) {
$fileMD = Get-FileMetaData -folder $PPTtemplates
$collection += $fileMD | select path, company
}
$OKCounter = 0
for ($i=0; $i -lt $Path.length; $i++) {
foreach ($obj in $collection) {
If ($Path[$i] -eq $obj.path -and $obj.company -eq $TemplateVersions) {$OKCounter++}
}
}
if ($OKCounter -eq $path.length) {
write-host "all files accounted for!"
}
[/code]
I then posted this code into the Detection Model of the application resulting in something similar to the following image:
If the application has delivered successfully (and the script results in ‘Exit Code = 0’ and a ‘write-host = “all files accounted for!”‘ piping to the ‘Stdout’ object, then the following entry (critical values highlighted in red text below) should appear in the local SCCM client log: C:\Windows\CCM\Logs\AppEnforce.log:
<![LOG[ Looking for exit code 0 in exit codes table…]LOG]!><time=”12:29:13.852-600″ date=”08-08-2014″ component=”AppEnforce” context=”” type=”1″ thread=”2144″ file=”appexcnlib.cpp:505″>
<![LOG[ Matched exit code 0 to a Success entry in exit codes table.]LOG]!><time=”12:29:13.853-600″ date=”08-08-2014″ component=”AppEnforce” context=”” type=”1″ thread=”2144″ file=”appexcnlib.cpp:584″>
<![LOG[ Performing detection of app deployment type User Install – Prod – Office 2010 Templates 5.0.2(ScopeId_92919E2B-F457-4BBD-82FF-0765C1E1E696/DeploymentType_0f69fa14-549d-4397-8a0b-004f0d0e85e7, revision 4) for user.]LOG]!><time=”12:29:13.861-600″ date=”08-08-2014″ component=”AppEnforce” context=”” type=”1″ thread=”2144″ file=”appprovider.cpp:2079″>
<![LOG[+++ Discovered application [AppDT Id: ScopeId_92919E2B-F457-4BBD-82FF-0765C1E1E696/DeploymentType_0f69fa14-549d-4397-8a0b-004f0d0e85e7, Revision: 4]]LOG]!><time=”12:29:16.977-600″ type=”1″ date=”08-08-2014″ file=”scripthandler.cpp:491″ thread=”2144″ context=”” component=”AppEnforce”>
<![LOG[++++++ App enforcement completed (10 seconds) for App DT “User Install – Prod – Office 2010 Templates 5.0.2″ [ScopeId_92919E2B-F457-4BBD-82FF-0765C1E1E696/DeploymentType_0f69fa14-549d-4397-8a0b-004f0d0e85e7], Revision: 4, User SID: S-1-5-21-1938088289-184369731-1547471778-5113] ++++++]LOG]!><time=”12:29:16.977-600″ date=”08-08-2014″ component=”AppEnforce” context=”” type=”1″ thread=”2144″ file=”appprovider.cpp:2366″>
We should also see a status of ‘Installed’ in the ‘Software Center’ application (part of the SCCM client):
Hope this helps with using SCCM application and Detection Method scripting! Any questions, please comment on my post below and I’ll endeavour to get back to you.
I believe this article gave me the best answers on how to get script detection Methods working than anything else I have found.
Well done and thanks for sharing!
Hi Michael
Do you know how and when SCCM recognizes modifications to the detection method script?
I suspect that I can not force a modified (i.e. corrected) detection script to be used by SCCM when nothing else in the app content and it’s SCCM properties changes.
Even manually updating the app revision in SCCM doesn’t help.
Initiating a machine policy would help you to full the latest changes made in Application.
If you’re wondering what context SCCM runs these detection scripts in. The answer is complicated. See here: http://serverfault.com/questions/699705
I had this as a concept for monitoring and maintaining an installation that is required on our machines.
In trying to find out who often an application evaluation is run I came across this Blog.
Thanks
Still trying to find out how often an application will verify/run the detection method..
Here is a good article about getting the correct version information from a file:
https://blogs.technet.microsoft.com/askpfeplat/2014/12/07/how-to-correctly-check-file-versions-with-powershell/
Has anyone else noticed that Application dectection scripts run before Application requirements are evaluated ? For example on a Dell machine, my HP detection script was running and failing even though I had a requirement that said hardware manufacture must equall Dell